﻿using Common;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace NewDevice.TCPIP
{
    public class PqwIoTcp : IDisposable
    {
        #region 配置参数

        private readonly string _ipAddress;
        private readonly int _port;
        private readonly int _inputChannelCount;
        private readonly int _outputChannelCount;

        // 缓存字段（线程安全）
        private volatile ImmutableDictionary<int, bool> _inputStatusCache = ImmutableDictionary<int, bool>.Empty;

        private volatile ImmutableDictionary<int, bool> _outputStatusCache = ImmutableDictionary<int, bool>.Empty;

        // 后台更新任务管理
        private readonly CancellationTokenSource _refreshCts = new();

        private Task? _refreshTask;

        // 同步对象
        private readonly object _commsLock = new();

        private TcpClient? _tcpClient;
        private NetworkStream? _stream;
        private const int _maxRetries = 10;

        public string DeviceName { get; set; } = nameof(PqwIoTcp);

        // 字节数计算
        private int InputByteCount => (_inputChannelCount + 7) / 8;

        private int OutputByteCount => (_outputChannelCount + 7) / 8;

        #endregion 配置参数

        #region 构造函数与初始化

        public PqwIoTcp(string ip = "192.168.10.10", int port = 502,
                        int inputChannels = 64, int outputChannels = 64)
        {
            _ipAddress = ip;
            _port = port;
            _inputChannelCount = inputChannels;
            _outputChannelCount = outputChannels;
        }

        /// <summary>
        /// 启动后台状态更新任务
        /// </summary>
        /// <param name="refreshInterval">刷新间隔(毫秒)</param>
        public void StartBackgroundRefresh(int refreshInterval = 100)
        {
            if (_refreshTask != null) return;

            var token = _refreshCts.Token;
            _refreshTask = Task.Run(async () =>
            {
                while (!token.IsCancellationRequested)
                {
                    try
                    {
                        RefreshAllStatus();
                        await Task.Delay(refreshInterval, token);
                    }
                    catch (OperationCanceledException)
                    {
                        break;
                    }
                }
            }, token);
            MyLog.Info($"{DeviceName}DI Monitor Start");
        }

        #endregion 构造函数与初始化

        #region 连接管理

        private void EnsureConnected()
        {
            if (_tcpClient?.Connected ?? false) return;

            lock (_commsLock)
            {
                if (_tcpClient?.Connected ?? false) return;

                Disconnect();
                _tcpClient = new TcpClient();
                _tcpClient.Connect(_ipAddress, _port);
                _stream = _tcpClient.GetStream();
            }
            MyLog.Info($"{DeviceName}已连接");
        }

        private async Task EnsureConnectedAsync()
        {
            if (_tcpClient?.Connected ?? false) return;

            if (_tcpClient?.Connected ?? false) return;

            Disconnect();
            _tcpClient = new TcpClient();
            await _tcpClient.ConnectAsync(_ipAddress, _port);
            _stream = _tcpClient.GetStream();

            MyLog.Info($"{DeviceName}已连接");
        }

        public void Connect()
        {
            EnsureConnected();
            ReadOutputStatus();
            ReadInputStatus();
            //StartBackgroundRefresh(50);
        }

        public async Task ConnectAsync()
        {
            await EnsureConnectedAsync();
            ReadOutputStatus();
            ReadInputStatus();
            //StartBackgroundRefresh(50);
        }

        public void Disconnect()
        {
            lock (_commsLock)
            {
                _stream?.Close();
                _tcpClient?.Close();
                _stream = null;
                _tcpClient = null;
            }
        }

        #endregion 连接管理

        #region 核心通信方法

        private byte[] Request(byte[] request, int expectedLength)
        {
            lock (_commsLock)
            {
                EnsureConnected();

                MyLog.DeviceData(deviceName: DeviceName, sendData: BitConverter.ToString(request).Replace("-", " "));
                // 发送请求
                _stream!.Write(request, 0, request.Length);

                // 接收响应
                var response = new byte[expectedLength];
                int totalRead = 0;
                while (totalRead < expectedLength)
                {
                    int read = _stream.Read(response, totalRead, expectedLength - totalRead);
                    MyLog.DeviceData(deviceName: DeviceName, receivedData: BitConverter.ToString(response).Replace("-", " "));
                    if (read == 0) throw new IOException("Connection closed");
                    totalRead += read;
                }

                return response;
            }
        }

        #endregion 核心通信方法

        #region 状态读取与缓存更新

        /// <summary>
        /// 刷新所有状态（输入+输出）
        /// </summary>
        public void RefreshAllStatus()
        {
            ReadInputStatus();
            //ReadOutputStatus();
        }

        /// <summary>
        /// 读取输入状态并更新缓存
        /// </summary>
        public void ReadInputStatus()
        {
            var request = new byte[] {
                0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x02,
                0x00, 0x00, (byte)(_inputChannelCount >> 8), (byte)_inputChannelCount
            };

            var response = Request(request, 9 + InputByteCount);
            var statusBytes = response.Skip(9).Take(InputByteCount).ToArray();
            var statusArray = ParseBitArray(statusBytes, _inputChannelCount);

            // 原子更新缓存
            var builder = ImmutableDictionary.CreateBuilder<int, bool>();
            for (int i = 0; i < statusArray.Length; i++)
            {
                builder.Add(i, statusArray[i]);
            }
            _inputStatusCache = builder.ToImmutable();
        }

        /// <summary>
        /// 读取输出状态并更新缓存
        /// </summary>
        private void ReadOutputStatus()
        {
            var request = new byte[] {
                0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x01,
                0x00, 0x00, (byte)(_outputChannelCount >> 8), (byte)_outputChannelCount
            };

            var response = Request(request, 9 + OutputByteCount);
            var statusBytes = response.Skip(9).Take(OutputByteCount).ToArray();
            var statusArray = ParseBitArray(statusBytes, _outputChannelCount);

            // 原子更新缓存
            var builder = ImmutableDictionary.CreateBuilder<int, bool>();
            for (int i = 0; i < statusArray.Length; i++)
            {
                builder.Add(i, statusArray[i]);
            }
            _outputStatusCache = builder.ToImmutable();
        }

        // 字节数组转布尔数组（低位在前）
        private static bool[] ParseBitArray(byte[] data, int bitsCount)
        {
            var result = new bool[bitsCount];
            for (int i = 0; i < bitsCount; i++)
            {
                int byteIndex = i / 8;
                int bitIndex = i % 8;
                result[i] = (data[byteIndex] & (1 << bitIndex)) != 0;
            }
            return result;
        }

        #endregion 状态读取与缓存更新

        #region 公开的缓存访问接口（线程安全）

        /// <summary>
        /// 获取输入通道状态（从缓存读取）
        /// </summary>
        public bool GetInputStatus(int channel)
        {
            if (channel < 0 || channel >= _inputChannelCount)
                throw new ArgumentOutOfRangeException(nameof(channel));

            return _inputStatusCache.TryGetValue(channel, out bool status) ? status : false;
        }

        /// <summary>
        /// 获取输出通道状态（从缓存读取）
        /// </summary>
        public bool GetOutputStatus(int channel)
        {
            if (channel < 0 || channel >= _outputChannelCount)
                throw new ArgumentOutOfRangeException(nameof(channel));

            return _outputStatusCache.TryGetValue(channel, out bool status) ? status : false;
        }

        /// <summary>
        /// 获取所有输入状态快照（线程安全副本）
        /// </summary>
        public ImmutableDictionary<int, bool> GetAllInputStatus() => _inputStatusCache;

        /// <summary>
        /// 获取所有输出状态快照（线程安全副本）
        /// </summary>
        public ImmutableDictionary<int, bool> GetAllOutputStatus() => _outputStatusCache;

        #endregion 公开的缓存访问接口（线程安全）

        #region 通道控制

        /// <summary>
        /// 设置单个输出通道状态
        /// </summary>
        public void SetOutputChannel(int channelIndex, bool enable)
        {
            if (channelIndex < 0 || channelIndex >= _outputChannelCount)
                throw new ArgumentOutOfRangeException(nameof(channelIndex));

            byte status = (byte)(enable ? 0xFF : 0x00);
            var request = new byte[] {
            0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x05,
            0x00, (byte)channelIndex, status, 0x00
        };

            var response = Request(request, request.Length);
            if (!response.SequenceEqual(request))
                throw new Exception($"{DeviceName} SETDO command failed");

            for (int i = 0; i < _maxRetries; i++)
            {
                ReadOutputStatus();
                if (_outputStatusCache[channelIndex] == enable)
                {
                    return;
                }
                Thread.Sleep(10);
            }
            throw new Exception($"{DeviceName} SETDO read not equal");
        }

        /// <summary>
        /// 设置所有输出通道状态
        /// </summary>
        public void SetAllOutputs(bool enable)
        {
            byte status = (byte)(enable ? 0x01 : 0x00);
            var request = new byte[] {
            0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x06,
            0x00, 0xA3, 0x00, status
        };

            var response = Request(request, request.Length);
            if (!response.SequenceEqual(request))
                throw new Exception($"{DeviceName} SETALLDO command failed");

            for (int i = 0; i < _maxRetries; i++)
            {
                ReadOutputStatus();
                if (_outputStatusCache.Where(x => x.Value == enable).Count() == _outputChannelCount)
                {
                    return;
                }
            }
            throw new Exception($"{DeviceName} SETDO read not equal");
        }

        #endregion 通道控制

        #region 资源清理

        public void Dispose()
        {
            _refreshCts.Cancel();
            _refreshTask?.Wait(1000);
            _refreshCts.Dispose();
            Disconnect();
            GC.SuppressFinalize(this);
        }

        #endregion 资源清理
    }
}